package services

import (
	"easyShop/utils"
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"
)

type Snowflake struct {
	sync.Mutex         // 锁
	timestamp    int64 // 时间戳 ，毫秒
	DataCenterId int64 // 数据中心机房id
	WorkerId     int64 // 工作节点
	sequence     int64 // 序列号
}

const (
	epoch             = int64(1577808000000)                           // 设置起始时间(时间戳/毫秒)：2020-01-01 00:00:00，有效期69年
	timestampBits     = uint(41)                                       // 时间戳占用位数
	dataCenterIdBits  = uint(7)                                        // 数据中心id所占位数
	workerIdBits      = uint(7)                                        // 机器id所占位数
	sequenceBits      = uint(12)                                       // 序列所占的位数
	timestampMax      = int64(-1 ^ (-1 << timestampBits))              // 时间戳最大值
	dataCenterIdMax   = int64(-1 ^ (-1 << dataCenterIdBits))           // 支持的最大数据中心id数量
	workerIdMax       = int64(-1 ^ (-1 << workerIdBits))               // 支持的最大机器id数量
	sequenceMask      = int64(-1 ^ (-1 << sequenceBits))               // 支持的最大序列id数量
	workerIdShift     = sequenceBits                                   // 机器id左移位数
	dataCenterIdShift = sequenceBits + workerIdBits                    // 数据中心id左移位数
	timestampShift    = sequenceBits + workerIdBits + dataCenterIdBits // 时间戳左移位数
)

func (s *Snowflake) GetNewSnowflakeId() int64 {
	s.Lock()
	now := time.Now().UnixNano() / 1000000 // 转毫秒
	// 判断上次使用的时间戳和当前的是否一致，如果一致则把序列号+1，如果一致则序列号使用0
	if s.timestamp == now {
		// 当同一时间戳（精度：毫秒）下多次生成id会增加序列号
		s.sequence = (s.sequence + 1) & sequenceMask
		if s.sequence == 0 {
			// 如果当前序列超出12bit长度，则需要等待下一毫秒
			// 下一毫秒将使用sequence:0
			for now <= s.timestamp {
				now = time.Now().UnixNano() / 1000000
			}
		}
	} else {
		// 不同时间戳（精度：毫秒）下直接使用序列号：0
		s.sequence = 0
	}
	t := now - epoch
	if t > timestampMax {
		s.Unlock()
		fmt.Errorf("epoch must be between 0 and %d", timestampMax-1)
		return 0
	}
	// 把本次的时间戳（毫秒）给回结构体
	b := fmt.Sprintf("%b", t)
	fmt.Print("len=", len(b), "\n")
	fmt.Print("timestampShift=", timestampShift, "\n")

	s.timestamp = now
	r := int64((t)<<timestampShift | (s.DataCenterId << dataCenterIdShift) | (s.WorkerId << workerIdShift) | (s.sequence))
	s.Unlock()
	return r
}

func (s *Snowflake) GetDataCenterId(snowflakeId int64) int {
	// 获取雪花ID的二进制
	b := fmt.Sprintf("%b", snowflakeId)
	// 设置数据中心的二进制值在雪花ID的二进制数据内的位置
	start := len(b) - int(sequenceBits) - int(workerIdBits) - int(dataCenterIdBits)
	end := start + int(dataCenterIdBits)
	// 从雪花ID的二进制中截取出数据中心的数据
	subStr := b[start:end]
	// 把数据中心的二进制值前面无效的0去掉，二进制都是以1开头的
	binaryData := subStr[strings.IndexAny(subStr, "1"):]
	binaryDataInt, _ := strconv.Atoi(binaryData)
	return utils.BinaryToDecimal(binaryDataInt)
}

func (s *Snowflake) GetWorkerId(snowflakeId int64) int {
	// 获取雪花ID的二进制
	b := fmt.Sprintf("%b", snowflakeId)
	// 设置工作机器ID的二进制值在雪花ID的二进制数据内的位置
	start := len(b) - int(sequenceBits) - int(workerIdBits)
	end := start + int(workerIdBits)
	// 从雪花ID的二进制中截取出工作机器的数据
	subStr := b[start:end]
	// 把工作机器的二进制值前面无效的0去掉，二进制都是以1开头的
	binaryData := subStr[strings.IndexAny(subStr, "1"):]
	binaryDataInt, _ := strconv.Atoi(binaryData)
	return utils.BinaryToDecimal(binaryDataInt)
}

func (s *Snowflake) GetSnowflakeIdBinaryLen(snowflakeId int64) int {
	b := fmt.Sprintf("%b", snowflakeId)
	return len(b)
}
